package com.hemerasolutions.v2trainer.model.exercise;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Temporal;

import com.hemerasolutions.v2trainer.model.interfaces.BaseReferencedModelInterface;
import com.hemerasolutions.v2trainer.model.modality.Modality;
import com.hemerasolutions.v2trainer.model.multimedia.Photo;
import com.hemerasolutions.v2trainer.model.multimedia.Video;
import com.hemerasolutions.v2trainer.model.muscle.BodyRegion;
import com.hemerasolutions.v2trainer.model.muscle.Muscle;
import com.hemerasolutions.v2trainer.model.muscle.MuscleGroup;
import com.hemerasolutions.v2trainer.model.training.Training;
import com.hemerasolutions.v2trainer.model.base.BaseModel;
import com.hemerasolutions.v2trainer.model.exercise.executions.Execution;
import com.hemerasolutions.v2trainer.model.exercise.intensity.IntensityExecution;
import com.hemerasolutions.v2trainer.model.exercise.intensity.IntensityModTable;
import com.hemerasolutions.v2trainer.model.exercise.intensity.IntensityTable;
import com.hemerasolutions.v2trainer.model.exerciseGroup.ExerciseGroup;
import com.hemerasolutions.v2trainer.util.Utility;
import com.hemerasolutions.v2trainer.util.annotations.ReferencedModel;

@Entity
public class Exercise extends BaseModel implements BaseReferencedModelInterface {
 
	private static final long serialVersionUID = 1L;
	
	private static String nameKey = "Exercise";
	
    @Column(length=50)    
	private String name;

    @Column(length=180)	 
	private String description;

    @Column
	private int version = 1;
    
    @Column
    private boolean defaultExercise; 
    
    @Column
    private boolean technical;

    @Temporal(javax.persistence.TemporalType.TIMESTAMP)
	private Date createdAt;

    @ManyToOne
    private Modality modality;
    
    @ManyToOne
    private VersionableExercise versionableExercise;

    @OneToMany(cascade=CascadeType.ALL,orphanRemoval=true)
	private List<Photo> photos;

    @OneToMany(cascade=CascadeType.ALL,orphanRemoval=true)
	private List<Video> videos;
        
    @ManyToMany
	private List<Equipment> equipments;
	 
    @OneToMany(mappedBy="exercise", cascade=CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval=true)
    private List<ExerciseMuscle> exerciseMuscles; 	   
    
    @ManyToMany
    private List<Execution> executions;
    
    @OneToMany(mappedBy="exercise", cascade=CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval=true)
    private List<IntensityExecution> intensityExecutions;

    @ManyToMany(mappedBy="exercises",fetch=FetchType.LAZY)
    @ReferencedModel(compareOnChange = false)
    private List<ExerciseGroup> referencedExerciseGroups;
    
    @ManyToMany(mappedBy="usedExercises",fetch=FetchType.LAZY)
    @ReferencedModel(compareOnChange = false)
    private List<Training> referencedTrainings;
    
    
    
	public Exercise(Exercise other, VersionableExercise versionableExercise){
    	this();
    	this.name = other.name;
    	this.description = other.description;
    	this.defaultExercise = other.defaultExercise;
    	this.createdAt = Utility.getRoundedDate();
    	this.modality = versionableExercise.getModality();
    	this.versionableExercise = versionableExercise;
    	this.technical = other.technical;
		this.equipments.addAll(other.equipments);
    	this.executions.addAll(other.executions);
		
    	for(ExerciseMuscle mc:other.getExerciseMuscles()){
    		this.exerciseMuscles.add(new ExerciseMuscle(mc,this));
    	}
    	
    	for(IntensityExecution ie:other.intensityExecutions){
    		this.intensityExecutions.add(new IntensityExecution(ie,this));
    	}
    	
    	for(Photo photo:other.photos){
    		this.photos.add(new Photo(photo));
    	}
    	
    	for(Video video:other.videos){
    		this.videos.add(new Video(video));
    	}
    			
	}
    
    public Exercise(Exercise other,Modality modality){
    	this();
    	this.name = other.name;
    	this.description = other.description;
    	this.defaultExercise = other.defaultExercise;
    	this.createdAt = Utility.getRoundedDate();
    	this.modality = modality;
    	this.technical = other.technical;
		this.equipments.addAll(other.equipments);
    	this.executions.addAll(other.executions);
		
    	for(ExerciseMuscle mc:other.getExerciseMuscles()){
    		this.exerciseMuscles.add(new ExerciseMuscle(mc,this));
    	}
    	
    	for(IntensityExecution ie:other.intensityExecutions){
    		this.intensityExecutions.add(new IntensityExecution(ie,this));
    	}
    	
    	for(Photo photo:other.photos){
    		this.photos.add(new Photo(photo));
    	}
    	
    	for(Video video:other.videos){
    		this.videos.add(new Video(video));
    	}
    	
    }
    
 
	public Exercise() {
		super();
		
		photos = new ArrayList<Photo>();
		videos = new ArrayList<Video>();
		equipments = new ArrayList<Equipment>();
		exerciseMuscles = new ArrayList<ExerciseMuscle>();
		intensityExecutions = new ArrayList<IntensityExecution>();		
		executions = new ArrayList<Execution>();
	}
	
	public Exercise copy(){
		Exercise copy = new Exercise();

		copy.id	= this.id;
		copy.name = this.name;
		copy.description = this.description;
		copy.version = this.version;
		copy.uuid = this.uuid;
		copy.technical = this.technical;
		copy.createdAt = this.createdAt;
		copy.defaultExercise = this.defaultExercise;

		copy.versionableExercise = this.versionableExercise;
		copy.modality = this.modality;

		copy.executions.addAll(this.executions);
		copy.intensityExecutions.addAll(this.intensityExecutions);
		copy.exerciseMuscles.addAll(this.exerciseMuscles);

		copy.videos.addAll(this.videos);
		copy.photos.addAll(this.photos);
		
		return copy;
		
	
	}

	public String getBodyRegionsText(){
		String ret = "";
		List<BodyRegion> bds = new ArrayList<BodyRegion>();
		
		for(Muscle musc:this.getMuscles()){
			if(!bds.contains(musc.getMuscleGroup().getBodyRegion())){
				bds.add(musc.getMuscleGroup().getBodyRegion());
			}
		}

		for(BodyRegion bd:bds){
			if(ret.equals("")){
				ret = Utility.getText(bd.getName());
			}else{
				ret = ret + ", " + Utility.getText(bd.getName());
			}
		}
		
		return ret;
	}
	
	
	public String getMuscleGroupsText(){
		String ret = "";
		List<MuscleGroup> mgs = new ArrayList<MuscleGroup>();
		
		for(Muscle musc:this.getMuscles()){
			if(!mgs.contains(musc.getMuscleGroup())){
				mgs.add(musc.getMuscleGroup());
			}
		}

		for(MuscleGroup mg:mgs){
			if(ret.equals("")){
				ret = Utility.getText(mg.getName());
			}else{
				ret = ret + ", " + Utility.getText(mg.getName());
			}
		}
		
		return ret;
	}

	public String getMusclesText(){
		String ret = "";
		for(Muscle musc:this.getMuscles()){
			if(ret.equals("")){
				ret = Utility.getText(musc.getName());
			}else{
				ret = ret + ", " + Utility.getText(musc.getName());
			}
		}
		return ret;
	}

//====
	public String getSecBodyRegionsText(){
		String ret = "";
		List<BodyRegion> bds = new ArrayList<BodyRegion>();
		
		for(Muscle musc:this.getSecundaryMuscles()){
			if(!bds.contains(musc.getMuscleGroup().getBodyRegion())){
				bds.add(musc.getMuscleGroup().getBodyRegion());
			}
		}
	
		for(BodyRegion bd:bds){
			if(ret.equals("")){
				ret = Utility.getText(bd.getName());
			}else{
				ret = ret + ", " + Utility.getText(bd.getName());
			}
		}
		
		return ret;
	}
	
	
	public String getSecMuscleGroupsText(){
		String ret = "";
		List<MuscleGroup> mgs = new ArrayList<MuscleGroup>();
		
		for(Muscle musc:this.getSecundaryMuscles()){
			if(!mgs.contains(musc.getMuscleGroup())){
				mgs.add(musc.getMuscleGroup());
			}
		}
	
		for(MuscleGroup mg:mgs){
			if(ret.equals("")){
				ret = Utility.getText(mg.getName());
			}else{
				ret = ret + ", " + Utility.getText(mg.getName());
			}
		}
		
		return ret;
	}
	
	public String getSecMusclesText(){
		String ret = "";
		for(Muscle musc:this.getSecundaryMuscles()){
			if(ret.equals("")){
				ret = Utility.getText(musc.getName());
			}else{
				ret = ret + ", " + Utility.getText(musc.getName());
			}
		}
		return ret;
	}

	public void prepareIntensitys(List<IntensityModTable> intensityModTables){
		boolean found;
		
		IntensityModTable imt = null;
		List<IntensityExecution> iesAux = new ArrayList<IntensityExecution>();
		
		for(IntensityExecution iex:this.intensityExecutions){
			if(this.executions.contains(iex.getExecution())){
				iesAux.add(iex);
			}
		}
		
		this.intensityExecutions = iesAux;
		
		for(Execution ex:this.executions){
			IntensityExecution ie = null;
			for(IntensityExecution iex:this.intensityExecutions){
				if(iex.getExecution().equals(ex)){
					ie = iex;
					break;
				}
			}
			if(ie == null){
				ie = new IntensityExecution(this,ex);
				this.intensityExecutions.add(ie);
			}

			for(int i = 0; i < 6; i++){
				found = false;
				for(IntensityTable it :ie.getIntensityTables()){
					if(it.getLevel() == i){
						found = true; 
						break;
					}
				}
				if(!found){
					IntensityTable it = new IntensityTable(i,ie);
					for(int x = 0; x < intensityModTables.size(); x++  ){
						imt = intensityModTables.get(x);
						if(imt.getExecution().getName().equals(ex.getName())){
							it.setIntensitys(imt.getIntensityZones(it));
							break;
						}	
					}

					ie.getIntensityTables().add(it);
				}
			}
		}
	}

	
	public void prepareIntensitys() {
		boolean found;
		
		for(Execution ex:this.executions){
			IntensityExecution ie = null;
			for(IntensityExecution iex:this.intensityExecutions){
				if(iex.getExecution().getName().equals(ex.getName())){
					ie = iex;
					break;
				}
			}
			if(ie == null){
				ie = new IntensityExecution();
				ie.setExecution(ex);
				this.intensityExecutions.add(ie);
				
			}

			for(int i = 0; i < 6; i++){
				found = false;
				for(IntensityTable it :ie.getIntensityTables()){
					if(it.getLevel() == i){
						found = true; 
						break;
					}
				}
				if(!found){
					IntensityTable it = new IntensityTable();
					it.setLevel(i);
					it.setIntensityExecution(ie);
					ie.getIntensityTables().add(it);
				}
			}
		}
	}
	
	
	
	public static long getSerialversionuid() {
		return serialVersionUID;
	}


	public String getName() {
		return name;
	}

	public String getDescription() {
		return description;
	}

	public int getVersion() {
		return version;
	}

	public boolean isTechnical() {
		return technical;
	}

	public Date getCreatedAt() {
		return createdAt;
	}

	public Modality getModality() {
		return modality;
	}

	public List<Photo> getPhotos() {
		return photos;
	}

	public List<Video> getVideos() {
		return videos;
	}

	public List<Equipment> getEquipments() {
		return equipments;
	}

	public void setName(String name) {
		this.name = name;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public void setVersion(int version) {
		this.version = version;
	}

	public void setTechnical(boolean technical) {
		this.technical = technical;
	}

	public void setCreatedAt(Date createdAt) {
		this.createdAt = createdAt;
	}

	public void setModality(Modality modality) {
		this.modality = modality;
	}

	public void setPhotos(List<Photo> photos) {
		this.photos = photos;
	}

	public void setVideos(List<Video> videos) {
		this.videos = videos;
	}

	public void setEquipments(List<Equipment> equipments) {
		this.equipments = equipments;
	}

	public List<ExerciseGroup> getReferencedExerciseGroups() {
		return referencedExerciseGroups;
	}

	public void setReferencedExerciseGroups(
			List<ExerciseGroup> referencedExerciseGroups) {
		this.referencedExerciseGroups = referencedExerciseGroups;
	}

	public List<Training> getReferencedTrainings() {
		return referencedTrainings;
	}

	public void setReferencedTrainings(List<Training> referencedTrainings) {
		this.referencedTrainings = referencedTrainings;
	}

	public List<Muscle> getSecundaryMuscles() {
		List<Muscle> muscles = new ArrayList<Muscle>();
		for(ExerciseMuscle em : this.getExerciseMuscles()){
			if(!em.isPrimaryMuscle()){
				muscles.add(em.getMuscle());
			}
		}
		
		return muscles;
	}

	public List<Muscle> getMuscles() {
		List<Muscle> muscles = new ArrayList<Muscle>();
		for(ExerciseMuscle em : this.getExerciseMuscles()){
			if(em.isPrimaryMuscle()){
				muscles.add(em.getMuscle());
			}
		}
		
		return muscles;
	}
	
	
	public List<Muscle> getAllMuscles() {
		List<Muscle> muscles = new ArrayList<Muscle>();
		for(ExerciseMuscle em : this.getExerciseMuscles()){
			if(!muscles.contains(em.getMuscle())){
				muscles.add(em.getMuscle());
			}
		}
		
		return muscles;
	}	
	

	public List<ExerciseMuscle> getExerciseMuscles() {
		return exerciseMuscles;
	}

	public void setExerciseMuscles(List<ExerciseMuscle> exerciseMuscles) {
		this.exerciseMuscles = exerciseMuscles;
	}

	public List<Execution> getExecutions() {
		return executions;
	}

	public void setExecutions(List<Execution> executions) {
		this.executions = executions;
	}

	public boolean isDefaultExercise() {
		return defaultExercise;
	}

	public void setDefaultExercise(boolean defaultExercise) {
		this.defaultExercise = defaultExercise;
	}

	public List<IntensityExecution> getIntensityExecutions() {
		return intensityExecutions;
	}

	public void setIntensityExecutions(List<IntensityExecution> intensityExecutions) {
		this.intensityExecutions = intensityExecutions;
	}

	public List<BodyRegion> getBodyRegions(){
		List<BodyRegion> list = new ArrayList<BodyRegion>();
		
		for(MuscleGroup mg:this.getMuscleGroups()){
			if(!list.contains(mg.getBodyRegion())){
				list.add(mg.getBodyRegion());
			}
		}
		return list;
	}
	
	public List<MuscleGroup> getMuscleGroups(){
		List<MuscleGroup> list = new ArrayList<MuscleGroup>();

		for(Muscle musc:this.getMuscles()){
			if(!list.contains(musc.getMuscleGroup())){
				list.add(musc.getMuscleGroup());
			}
		}
		
		return list;
	}
	
	
	public VersionableExercise getVersionableExercise() {
		return versionableExercise;
	}

	public void setVersionableExercise(VersionableExercise versionableExercise) {
		this.versionableExercise = versionableExercise;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result
				+ ((createdAt == null) ? 0 : createdAt.hashCode());
		result = prime * result + (defaultExercise ? 1231 : 1237);
		result = prime * result
				+ ((description == null) ? 0 : description.hashCode());
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		result = prime * result
				+ ((modality == null) ? 0 : modality.hashCode());
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		result = prime * result + (technical ? 1231 : 1237);
		result = prime * result + ((uuid == null) ? 0 : uuid.hashCode());
		result = prime * result + version;
		result = prime
				* result
				+ ((versionableExercise == null) ? 0 : versionableExercise
						.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (getClass() != obj.getClass()) {
			return false;
		}
		Exercise other = (Exercise) obj;
		if (createdAt == null) {
			if (other.createdAt != null) {
				return false;
			}
		} else if (!createdAt.equals(other.createdAt)) {
			return false;
		}
		if (defaultExercise != other.defaultExercise) {
			return false;
		}
		if (description == null) {
			if (other.description != null) {
				return false;
			}
		} else if (!description.equals(other.description)) {
			return false;
		}
		if (id == null) {
			if (other.id != null) {
				return false;
			}
		} else if (!id.equals(other.id)) {
			return false;
		}
		if (modality == null) {
			if (other.modality != null) {
				return false;
			}
		} else if (!modality.equals(other.modality)) {
			return false;
		}
		if (name == null) {
			if (other.name != null) {
				return false;
			}
		} else if (!name.equals(other.name)) {
			return false;
		}
		if (technical != other.technical) {
			return false;
		}
		if (uuid == null) {
			if (other.uuid != null) {
				return false;
			}
		} else if (!uuid.equals(other.uuid)) {
			return false;
		}
		if (version != other.version) {
			return false;
		}
		if (versionableExercise == null) {
			if (other.versionableExercise != null) {
				return false;
			}
		} else if (!versionableExercise.equals(other.versionableExercise)) {
			return false;
		}
		return true;
	}

	@Override
	public String toString() {
		return "Exercise [id=" + id + ", name=" + name + ", description="
				+ description + ", version=" + version + ", defaultExercise="
				+ defaultExercise + ", technical=" + technical + ", createdAt="
				+ createdAt + ", uuid=" + uuid + ", modality=" + modality
				+ ", versionableExercise=" + versionableExercise + "]";
	}

	@Override
	public String getNameKey() {
		return nameKey;
	}

	@Override
	public void fillReferences() {
		// Nothing to do
	}
}
 
